﻿using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Windows.Controls;
using System;
using SHomeWorkshop.LunarPresentation.Adorners;

namespace SHomeWorkshop.LunarPresentation.Commands
{
    public partial class Command
    {
        /// <summary>
        /// 将当前文档导出为图片。
        /// </summary>
        public static void OutportDocumentToImage()
        {
            if (Globals.selectedEditor == null)
            {
                System.Windows.MessageBox.Show("当前没有打开任何文档，无法导出图片文件！",
                    Globals.appName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            //string filename = Globals.selectedEditor.FileNameHeader.Text.ToLower();
            //if (filename.EndsWith(".lpdoc"))
            //    filename = filename.Substring(0, filename.Length - 6);

            OutportExpressionToImage(Globals.selectedEditor.RootPanel, "文档图");
        }

        public static void OutportExpressionToImage()
        {
            if (Globals.selectedEditor == null)
            {
                System.Windows.MessageBox.Show("请先选中要导出为图片文件的公式！",
                    Globals.appName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            OutportExpressionToImage(Globals.selectedEditor.SelectedExpression, "公式图");
        }

        /// <summary>
        /// 导出图像为文件。
        /// </summary>
        /// <param name="outportExpression">目标公式。如果是整个文档导出，应传入根面板。</param>
        /// <param name="defaultFilename">不应带路径。</param>
        private static void OutportExpressionToImage(Expressions.Expression outportExpression,
            string defaultFilename, string defFullPathFilename = null)
        {
            if (outportExpression == null) return;

            // 添加个消息框，提示一下要不要改为“柔和方式”渲染。
            if (Globals.MainWindow == null)
            {
                System.Windows.MessageBox.Show("发生意外，导出图片前竟然找不到主窗口！", Globals.appName, MessageBoxButton.OK,
                    MessageBoxImage.Stop);
                return;
            }

            TextFormattingMode tfMode = TextOptions.GetTextFormattingMode(Globals.MainWindow);
            if (defFullPathFilename == null && tfMode != TextFormattingMode.Ideal)
            {
                MessageBoxResult mbResult = Dialogs.LunarMsgBox.MsgBoxShow(
                    Globals.appName,
                    "导出的图片可能有较明显的锯齿，要继续吗？",
                    "当前文档使用的渲染方式是“锐利”，" +
                     "这种渲染方式适合用在“字号较小、在显示器上显示”的场合；" +
                     "如果用以导出图像，其中的文字将会带有明显的锯齿。",
                     "要继续导出请点击“是（Yes）”；" +
                     "否则请点击“否（No）”，并使用“视图→文字渲染方式”菜单将渲染方式改为“柔和”之后再重新执行此命令！",
                     MessageBoxButton.YesNo, MessageBoxImage.Information);
                if (mbResult != MessageBoxResult.Yes) return;
            }

            if (Globals.selectedEditor.RootPanel.MaxWidth != double.MaxValue)
            {
                MessageBox.Show("　　此文档档限制了根面板（页面）的最大宽度，可能造成文档的某些部分无法显示。\r\n" +
                "　　导出前请确定当前显示的内容正是所需要的内容。\r\n" +
                "　　要取消此限制，请使用命令：zdymk_Max",
                    Globals.appName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }

            string filename;

            if (defFullPathFilename == null)
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Title = Globals.appName + "——导出图片：";
                sfd.Filter = "标记图像格式(*.tiff)|*.tiff|可移植网络图形(*.png)|*.png";

                sfd.FileName = defaultFilename;//无路径。

                string defFileFullPath = "";
                string defFileNamePrefix = "";

                if (outportExpression.Editor.FileName == null ||
                    outportExpression.Editor.FileName == "")
                {
                    defFileFullPath = GetDefFileNameOfEditor(outportExpression.Editor);
                }
                else
                {
                    if (outportExpression.Editor.FileName.EndsWith(".lpdoc"))
                    {
                        defFileFullPath = outportExpression.Editor.FileName.Substring(0,
                            outportExpression.Editor.FileName.Length - 6);
                        int i = defFileFullPath.LastIndexOf("\\");

                        if (i > 0 && i < defFileFullPath.Length)
                        {
                            defFileNamePrefix = defFileFullPath.Substring(i + 1);//注意这个顺序。
                            defFileFullPath = defFileFullPath.Substring(0, i);
                        }
                    }
                    else
                    {
                        defFileFullPath = outportExpression.Editor.FileName;
                        if (defFileFullPath.EndsWith("\\"))
                        {
                            defFileFullPath = defFileFullPath.Substring(0, defFileFullPath.Length - 1);
                        }

                        int i = defFileFullPath.LastIndexOf("\\");
                        if (i >= 0)
                        {
                            defFileFullPath = defFileFullPath.Substring(0, i);
                        }
                    }
                }

                if (defFileNamePrefix.Length <= 0)
                {
                    if (outportExpression.Editor.FileNameHeader.Text.EndsWith(".lpdoc"))
                    {
                        defFileNamePrefix = outportExpression.Editor.FileNameHeader.Text.Substring(0,
                            outportExpression.Editor.FileNameHeader.Text.Length - 6);
                    }
                    else
                    {
                        defFileNamePrefix = outportExpression.Editor.FileNameHeader.Text;
                    }
                }

                sfd.FileName = defFileNamePrefix + "_" + defaultFilename;
                sfd.InitialDirectory = defFileFullPath;

                Globals.SwitchInputMethod(true);
                bool? result = sfd.ShowDialog();

                if (result != true)
                {
                    outportExpression.Editor.ParentWindow.Focus();
                    return;
                }

                filename = sfd.FileName;
            }
            else
            {
                filename = defFullPathFilename;
            }

            try
            {
                RenderTargetBitmap rtb;

                //BitmapEncoder encoder = new PngBitmapEncoder();//分辨率只能是96DPI。
                //BitmapEncoder encoder = new GifBitmapEncoder();//分辨率过低。
                //BitmapEncoder encoder = new JpegBitmapEncoder();//分辨率可以是300DPI，但不支持透明底色。
                BitmapEncoder encoder;

                bool isPng = filename.EndsWith(".png");
                if (isPng)
                {
                    rtb = GetExpressionImage(outportExpression, false);//96DPI
                    encoder = new PngBitmapEncoder();
                }
                else
                {
                    rtb = GetExpressionImage(outportExpression);//300DPI
                    encoder = new TiffBitmapEncoder();
                }

                encoder.Frames.Add(BitmapFrame.Create(rtb));
                using (Stream stm = File.Create(filename))
                {
                    encoder.Save(stm);
                }

                if (isPng)
                {
                    using (StreamWriter sw = File.AppendText(filename))
                    {
                        sw.WriteLine();

                        if (outportExpression.RootPanel == null ||
                            outportExpression.RootPanel == outportExpression)
                        {
                            sw.WriteLine("<?Lunar Presentation?>");
                            sw.Write(Globals.selectedEditor.XmlDocument.OuterXml);
                        }
                        else
                        {
                            if (outportExpression is Expressions.SubPanel)
                            {
                                sw.WriteLine("<?Lunar Presentation SubPanelEx?>");
                                sw.Write(outportExpression.XmlData.OuterXml);
                            }
                            else
                            {
                                sw.WriteLine("<?Lunar Presentation NormalEx?>");
                                sw.Write(outportExpression.XmlData.OuterXml);
                            }
                        }

                        if (defFullPathFilename == null)
                        {
                            if (outportExpression.RootPanel == null ||
                                outportExpression.RootPanel == outportExpression)
                            {
                                System.Windows.MessageBox.Show("已导出为：" + filename +
                                "\r\n\r\n导出为PNG格式的图片会嵌入文档数据，可以使用本程序重新打开此文档！\r\n" +
                                "★★请勿使用图像编辑软件对导出的 PNG 图片进行编辑，这会破坏该图片文件尾部附加的文档数据。\r\n\r\n" +
                                "★★Tiff格式的图片主要用以打印，【不支持】此功能！",
                                Globals.appName,
                                MessageBoxButton.OK, MessageBoxImage.Information);
                            }
                            else
                            {
                                Expressions.BaseText outBt = outportExpression as Expressions.BaseText;
                                if (outBt != null && outBt.IsCollapsed && isPng)
                                {
                                    System.Windows.MessageBox.Show("已导出为：" + filename + "\r\n\r\n" +
                                            "★请注意：此次导出的是一个折叠的标题文本块，" +
                                            "在导出的png公式图中，只附加了此标题文本块本身的Xml数据。" +
                                            "因此不能指望用此图片文件来尝试恢复被此标题文本块折叠起来的其它公式。",
                                            Globals.appName, MessageBoxButton.OK, MessageBoxImage.Information);
                                }
                                else
                                {
                                    System.Windows.MessageBox.Show("已导出为：" + filename, Globals.appName,
                                        MessageBoxButton.OK, MessageBoxImage.Information);
                                }
                            }
                        }
                        else//如果是打开导出的png文档图，并编辑后，又保存。
                        {
                            outportExpression.Editor.IsModified = false;
                            outportExpression.Editor.ModifingsList.PointOfSaving =
                                outportExpression.Editor.ModifingsList.Point;
                        }
                    }
                }
                else
                {
                    System.Windows.MessageBox.Show("已导出为：" + filename +
                        "\r\n\r\n★注意：导出为Tiff格式的图片【不会】嵌入文档数据，因此导出后【不能再用本程序打开】！",
                        Globals.appName, MessageBoxButton.OK, MessageBoxImage.Information);
                }
            }
            finally
            {
                // 为防止主窗口快捷键失效，令Editor的ScrollViewer获取焦点。
                Globals.MainWindow.Focus();

                Globals.SwitchInputMethod(false);
            }
        }

        /// <summary>
        /// 将指定的公式导出为一张图片。
        /// </summary>
        /// <param name="outportExpression">要导出图片的公式。</param>
        /// <param name="withClearnessDPI">是否按照较高的DPI导出（用于打印的图像应使用此值）。
        /// 为True时使用300DPI，为False时使用96DPI。</param>
        /// <returns></returns>
        public static RenderTargetBitmap GetExpressionImage(Expressions.Expression outportExpression,
            bool withClearnessDPI = true)
        {
            if (outportExpression.Editor == null)
            {
                System.Windows.MessageBox.Show("发生意外错误！公式没有编辑器。",
                    Globals.appName, MessageBoxButton.OK, MessageBoxImage.Stop);
                return null;
            }

            //Brush back = outportExpression.Editor.BaseScrolledViewer.Background;
            //Brush exBorderBsh = outportExpression.MainBorder.BorderBrush;

            //outportExpression.MainBorder.BorderBrush = back;

            RenderTargetBitmap rtb;
            if (withClearnessDPI)
            {
                rtb = new RenderTargetBitmap(
                    (int)(outportExpression.ActualWidth * 3.125), (int)(outportExpression.ActualHeight * 3.125),
                    300, 300, PixelFormats.Pbgra32);//300÷96=3.125//用来打印的图片分辨率要高些。96DPI只适合屏幕显示。
            }
            else
            {
                rtb = new RenderTargetBitmap(
                    (int)outportExpression.ActualWidth, (int)outportExpression.ActualHeight,
                    96, 96, PixelFormats.Pbgra32);//只在屏幕上显示的图片分辨率可以低些，96DPI就够了。
            }

            if (outportExpression is Expressions.StackPanelExpression)
            {
                Expressions.StackPanelExpression pEx = outportExpression as Expressions.StackPanelExpression;

                Brush preBack = pEx.ExBackground;
                if (preBack == Brushes.Transparent)
                {
                    pEx.BasePanel.Background = pEx.Editor.DefaultBackground;
                }

                pEx.InvalidateArrange();
                pEx.UpdateLayout();
                rtb.Render(outportExpression.Child as Visual);

                if (preBack == Brushes.Transparent)
                {
                    pEx.BasePanel.Background = Brushes.Transparent;
                }
            }
            else
            {
                Brush preBack = outportExpression.MainBorder.Background;
                if (preBack == Brushes.Transparent)
                {
                    outportExpression.MainBorder.Background = outportExpression.Editor.DefaultBackground;
                }

                outportExpression.MainBorder.InvalidateArrange();
                outportExpression.MainBorder.UpdateLayout();
                rtb.Render(outportExpression.Child as Visual);

                if (preBack == Brushes.Transparent)
                {
                    outportExpression.MainBorder.Background = preBack;
                }
            }

            //置回边框色。
            //outportExpression.MainBorder.BorderBrush = exBorderBsh;

            return rtb;
        }

        public static void OutportSplitedImages(Editor masterEditor)
        {
            if (masterEditor == null) return;

            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = Globals.appName + "——导出图片：";
            sfd.Filter = "标记图像格式(*.tiff)|*.tiff";

            sfd.FileName = masterEditor.FileNameHeader.Text;//无路径。

            string defFileFullPath = "";
            string defFileNamePrefix = masterEditor.FileNameHeader.Text;

            int indexOfDot = defFileNamePrefix.LastIndexOf('.');
            if (indexOfDot > -1)
            {
                defFileNamePrefix = defFileNamePrefix.Substring(0, indexOfDot);
            }

            sfd.FileName = defFileNamePrefix + "_" + "文档图切片";
            sfd.InitialDirectory = defFileFullPath;

            Globals.SwitchInputMethod(true);
            bool? result = sfd.ShowDialog();

            if (result != true)
            {
                masterEditor.ParentWindow.Focus();
                return;
            }

            string filename;

            if (sfd.FileName.EndsWith(".tiff"))
            {
                filename = sfd.FileName.Substring(0, sfd.FileName.Length - 5);
            }
            else { filename = sfd.FileName; }
            //这个对话框应先弹出来，否则会令用户无所适从。

            System.Windows.Media.Imaging.RenderTargetBitmap rtb = Commands.Command.GetExpressionImage(masterEditor.RootPanel);

            if (rtb == null)
            {
                MessageBox.Show("发生意外错误，未能获取当前文档的图形，无法继续打印！",
                    Globals.appName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            // PixelFormats.Pbgra32

            Rect wholeRect = new Rect(0, 0, rtb.PixelWidth, rtb.PixelHeight);

            PaperInfo curPaperInfo = PaperInfo.GetPaperInfo(
                masterEditor.PaperSizeLineAdorner.PaperName);

            int width, height;
            if (masterEditor.PaperSizeLineAdorner.PaperDirection == Orientation.Horizontal)
            {
                width = curPaperInfo.LongSidePixcelCount;
                height = curPaperInfo.ShortSidePixcelCount;
            }
            else
            {
                height = curPaperInfo.LongSidePixcelCount;
                width = curPaperInfo.ShortSidePixcelCount;
            }

            //取出页面宽、高
            int paperWidth = width;
            int paperHeight = height;

            Thickness margin = new Thickness(
                masterEditor.PageSettingInfo.MarginLeft,
                masterEditor.PageSettingInfo.MarginTop,
                masterEditor.PageSettingInfo.MarginRight,
                masterEditor.PageSettingInfo.MarginBottom
                );

            //Wpf是96DPI，而我要打印的图像是300DPI。3.25是比例值。
            width = (int)(width - margin.Left * 3.25 - margin.Right * 3.25);
            height = (int)(height - margin.Top * 3.25 - margin.Bottom * 3.25);

            int offcetX = 0, offcetY = 0;

            bool endX = false;
            bool endY = false;
            try
            {
                List<BitmapSource> bitmapSources = new List<BitmapSource>();

                do
                {
                    int realHeight;

                    if (offcetY + height > wholeRect.Height)
                    {
                        realHeight = (int)wholeRect.Height - offcetY;
                        endY = true;
                    }
                    else
                    {
                        realHeight = height;
                    }

                    do
                    {
                        int realWidth;

                        if (offcetX + width > wholeRect.Width)
                        {
                            realWidth = (int)wholeRect.Width - offcetX;
                            endX = true;
                        }
                        else
                        {
                            realWidth = width;
                        }

                        Int32Rect pieceRect;

                        if (width <= 0 || height <= 0) break;

                        pieceRect = new Int32Rect(offcetX, offcetY, realWidth, realHeight);
                        var array = new byte[realWidth * realHeight * rtb.Format.BitsPerPixel / 8];//每像素需要32位（4字节）。

                        rtb.CopyPixels(pieceRect, array, realWidth * rtb.Format.BitsPerPixel / 8, 0);

                        //跨度，每行像素数据的总字节数
                        //int stride = rtb.PixelWidth * rtb.Format.BitsPerPixel / 8;
                        //stride += 4 - stride % 4; //补上几个字节，使其可以被4整除 

                        BitmapSource newBitmapSource = BitmapImage.Create(realWidth, realHeight, 300, 300,
                            PixelFormats.Pbgra32, null, array, realWidth * rtb.Format.BitsPerPixel / 8);

                        bitmapSources.Add(newBitmapSource);

                        offcetX += width;

                    } while (offcetX <= wholeRect.Width && endX == false);

                    offcetY += height;
                    offcetX = 0; endX = false;
                } while (offcetY <= wholeRect.Height && endY == false);

                //向磁盘循环写出所有图片文件。            
                for (int i = 1; i <= bitmapSources.Count; i++)
                {
                    BitmapSource bitmapSource = bitmapSources[i - 1];
                    TiffBitmapEncoder encoder = new TiffBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
                    using (Stream stm = File.Create(filename + "_" + i.ToString("D4") + ".tiff"))
                    {
                        encoder.Save(stm);
                    }
                }

                MessageBox.Show("图像切片已全部导出。", Globals.appName,
                    MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                Dialogs.LunarMsgBox.MsgBoxShow(
                        Globals.appName,
                        "未能顺利导出全部图像切片。",
                        "异常信息如下：\r\n" + ex.Message,
                        ex.StackTrace);
            }
        }
    }
}
